#!/bin/bash
###########################################################################
# SONIC Factory reset script                                              #
# /usr/bin/reset-factory                                                  #
# This script is used to reset the system to factory settings.            #
# It creates factory default configuration and save it to config_db.json. #
# Also, it clear logs, tech-support, reboot-cause files, warmboot files,  #
# docker containers non-default users, users history files and            #
# home directories.                                                       #
###########################################################################

# Initialize constants
CONFIG_DB_JSON=/etc/sonic/config_db.json
DEFAULT_USERS_FILE=/etc/sonic/default_users.json
PERMLOG=/var/log/systemlog
SONIC_VERSION=$(sonic-cfggen -y /etc/sonic/sonic_version.yml -v build_version)
SONIC_OVERLAY_UPPERDIR="/host/image-$SONIC_VERSION/rw/etc/sonic"

SERVICES_STOPPED=0
trap "error_cleanup" HUP INT QUIT PIPE TERM

# Command usage and help
usage()
{
    cat << EOF
 Usage:  reset-factory < keep-all-config | only-config | keep-basic >

        Create factory default configuration and save it to
        to ${CONFIG_DB_JSON}.
        Clears logs, system files and reboot the system.

        Default          - Reset configurations to factory default. Logs and files will be deleted.
        keep-all-config  - Preserves all configurations after boot. Logs and files will be deleted.
        only-config      - Reset configurations to factory default. Logs and files will be preserved.
        keep-basic       - Preserves basic configurations only after boot. Logs and files will be deleted.
EOF
}

run_reboot()
{
    reboot
    # If for any reason we reach this code, then force reboot
    rc=$?
    if [ $rc -ne 0 ]; then
        # Force reboot
        reboot -f
    fi
}

error_cleanup()
{
    if [ ! -z "${TEMP_CFG}" ]; then
        # Recover config_db.json file
        mv ${TEMP_CFG} ${CONFIG_DB_JSON}
    fi

    if [ $SERVICES_STOPPED -eq 0 ]; then
        ERRMSG="reset-factory: halted with error before stopping critical services; exiting"
        logger $ERRMSG
        echo $ERRMSG
        exit 1
    else
        ERRMSG="reset-factory: halted with error after stopping critical services; rebooting"
        logger $ERRMSG
        echo $ERRMSG
        run_reboot
    fi
}

# Restore original /etc/sonic folder by clearing the folder in overlayfs upperdir
clear_sonic_dir()
{
    EXCLUDE_LIST="${CONFIG_DB_JSON}\|/etc/sonic/sonic-environment"
    find $SONIC_OVERLAY_UPPERDIR -type f | grep -ve ${EXCLUDE_LIST} | xargs rm -rf
    # remount root
    mount -o remount /
}

# Get list of defaults users names and passwords from DEFAULT_USERS_FILE
# Delete non-default users and restore default password of default users
reset_users()
{
    if [ ! -f "${DEFAULT_USERS_FILE}" ]; then
        echo "Error: Failed to get default users information"
        return
    fi
    # Get default user accounts
    default_users=$(jq -r '. | keys[]' $DEFAULT_USERS_FILE)
    EXCLUDE_LIST=$(echo $default_users | tr ' ' '|')
    # Get non-default user accounts
    other_users=$(getent passwd | awk -F:  '($3>=1000 && $3<=60000) {print $1}' | grep -E -v $EXCLUDE_LIST)
    echo "Remove non-default users"
    for user in ${other_users[@]}
    do
       # avoid printing home directory and mail spool errors
       userdel -rf $user 2> /dev/null
    done
    echo "Restore default users passwords"
    for user in ${default_users[@]}
    do
       # Restore default password
       user_pass=$(jq -r '.[$user].password' --arg user "${user}" $DEFAULT_USERS_FILE)
       echo "$user:$user_pass" | chpasswd -e
       # Check if we need to expire password
       expire=$(jq -r '.[$user].expire' --arg user "${user}" $DEFAULT_USERS_FILE)
       [ "${expire}" == "true" ] && passwd -e ${user}
    done
}

# Only root can run reset factory
if [ $UID != 0 ]; then
    echo "You must be root to reset system to factory settings"
    exit 1
fi

CMD=$1
FACTORY_TYPE=

if [ "$CMD" = "keep-all-config" ] || [ "$CMD" = "only-config" ] || \
   [ "$CMD" = "keep-basic" ] || [ -z "$CMD" ]; then
    FACTORY_TYPE=$CMD
else
    usage
    exit 1
fi

SERVICES_STOPPED=1
echo "Stop critical services"
monit unmonitor container_checker
systemctl stop sonic.target --job-mode replace-irreversibly

rc=$?
if [ $rc -ne 0 ]; then
    error_cleanup
fi

DATE=$(date "+%Y/%m/%d %H:%M:%S")
HOSTNAME=$(hostname | sed 's/\./ /' | awk '{print $1}')
printf "%s %s reset-factory: resetting system to factory defaults\n" "$DATE" "$HOSTNAME" >> $PERMLOG

# Backup and delete config_db.json
TEMP_CFG="/tmp/temp_config_db.$$"
cp ${CONFIG_DB_JSON} ${TEMP_CFG}
if [ "$FACTORY_TYPE" != "keep-basic" ] && [ "$FACTORY_TYPE" != "keep-all-config" ]; then
    rm -f ${CONFIG_DB_JSON}
fi

echo "Call config-setup factory"
config-setup factory $FACTORY_TYPE
rc=$?
if [ $rc -ne 0 ]; then
    error_cleanup
fi

if [ "$FACTORY_TYPE" != "only-config" ]; then

    if [ "$FACTORY_TYPE" != "keep-basic" ]; then

        # Delete non-default users and restore default users passwords
        reset_users

        echo "Delete bash, python and vim history files"
        find /home /root -type f -name ".bash_history" -o -name ".python_history" -o -name ".viminfo" | xargs rm -rf

        echo "Delete any non-dotfiles in users home directories"
        find /home/ /root -type f ! -iname ".*" -delete
    fi

    echo "Remove all docker containers except the database"
    database_pattern=($(docker ps -a -q -f "name=database" | paste -sd '|' -))
    docker rm -f $(docker ps -a -q | egrep -v ${database_pattern}) > /dev/null

    echo "Clear sonic directory"
    clear_sonic_dir

    echo "Clear warmboot folder"
    find /host/warmboot/ -type f -delete

    echo "Delete reboot-cause files and symlinks"
    find /host/reboot-cause/ -type l,f -delete

    echo "Delete tech-support files"
    rm -rf /var/dump/*

    echo "Delete logs files"
    find /var/log/ -type f ! -iname "wtmp" ! -iname "btmp" ! -iname "lastlog" ! -iname "systemlog" -delete

    # Clear wtmp, utmp and lastlog files
    rm -rf /var/log/btmp.*
    cat /dev/null > /var/log/btmp
    rm -rf /var/log/wtmp.*
    cat /dev/null > /var/log/wtmp
    rm -rf /var/log/lastlog.*
    cat /dev/null > /var/log/lastlog
fi

run_reboot
